/**
 * 
 */
package org.jinion.database.connectionPool;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author zhlmmc
 *
 */
public class ConnectionManager {
	private static final Log log = LogFactory.getLog(ConnectionManager.class);
	
	private static int MIN_CON = 3;
	private static int MAX_CON = 10;
	
	private LinkedList<MyConnection> connections;
	private int minCon = MIN_CON;
	private int maxCon = MAX_CON;
	private volatile int conCount = 0;
	
	private String driver;
	private String url;
	private String user;
	private String password;
	
	//private ThreadLocal<MyConnection> threadConnections = new ThreadLocal<MyConnection>();
	
	public ConnectionManager(String driver, String url, String user, String password) 
		throws ClassNotFoundException, SQLException {

		this(driver, url, user, password, MIN_CON, MAX_CON);
	}
	
	public ConnectionManager(String driver, String url, String user, String password, int minConnection, int maxConnection) 
		throws ClassNotFoundException, SQLException {
		
		this.driver = driver;
		this.url = url;
		this.user = user;
		this.password = password;
		
		if (minConnection >= 0) {
			this.minCon = minConnection;
		}
		
		if (maxConnection > 0) {
			this.maxCon = maxConnection;
		}
		
		connections = new LinkedList<MyConnection>();
		
		Class.forName(driver);
		for (int i = 0; i < minCon; i++) {
			MyConnection con = new MyConnection(DriverManager.getConnection(url, user, password));
			log.debug("New connection (id = " + con.getId() + ") is created.");
			
			connections.add(con);
			conCount++;
		}
		
		log.debug("ConnectionManager is initialized: ");
		log.debug("Driver: " + this.driver);
		log.debug("URL: " + this.url);
		log.debug("User: " + this.user);
		log.debug("Password: " + this.password);
		log.debug("Minimum Connection: " + this.minCon);
		log.debug("Maximum connection: " + this.maxCon);
	}
	
	public Connection getConnection() throws ClassNotFoundException, SQLException{
		
		synchronized (connections) {
			MyConnection myConn = null;
			if (myConn == null) {
				for (MyConnection temp : connections) {
					if (!temp.isInUse()) {
						myConn = temp;
						break;
					}
				}
				
				if (myConn == null && conCount < maxCon) {
					Class.forName(driver);
					Connection con = DriverManager.getConnection(url, user, password);
					myConn = new MyConnection(con);
					log.debug("New connection (id = " + myConn.getId() + ") is created.");
					connections.add(myConn);
					conCount++;
					return con;
				}
			}
			
			if (myConn != null) {
				myConn.setInUse(true);
				myConn.setLastAccess(System.currentTimeMillis());
				myConn.increaseUseCount();
				
				return myConn.getConn();
			}
			
			throw new SQLException("There is no free connection!");
		}
	}
	
	// TODO: if a connection is idle for a long time, close it!
	public void releaseConnection(Connection con){
		synchronized (connections) {
			for (MyConnection myCon : connections) {
				if (myCon.getConn() == con) {
					myCon.setInUse(false);
					// In case, client fails to commit a transaction or rollback.
					try {
						if (!con.getAutoCommit()) {
							con.rollback();
							con.setAutoCommit(true);
						}
					} catch (SQLException e) {
						// TODO: Should remove this connection from connection list.
						log.debug(e);
					}
					
					log.debug("Connection (id = " + myCon.getId() + ") is released.");
				}
			}
		}
	}
}
